package cn.sc.summer.mybatis.handler;

import cn.sc.summer.mybatis.context.DynamicDataSourceContext;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;

/**
 * 使用这个方案，有个小缺陷，就是我们默认commit/rollback方法，不会出现异常，当然这个概率的确是极小极小的
 */
public class RoutingProxyHandler implements InvocationHandler {

    private static final String METHOD_CLOSE = "close";
    private static final String METHOD_SET_AUTOCOMMIT = "setAutoCommit";
    private static final String METHOD_COMMIT = "commit";
    private static final String METHOD_ROLLBACK = "rollback";

    private final DynamicDataSourceContext dynamicDataSourceContext;

    private final Map<Object, Connection> connectionMap = new HashMap<>();

    public RoutingProxyHandler(DynamicDataSourceContext dynamicDataSourceContext) {
        this.dynamicDataSourceContext = dynamicDataSourceContext;
    }

    /**
     * 是否自动提交
     */
    private boolean autoCommit = true;

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //特殊方法处理
        {
            if (METHOD_CLOSE.equals(method.getName())) {
                doClose();
                return null;
            }
            if (METHOD_SET_AUTOCOMMIT.equals(method.getName())) {
                doSetAutoCommit(args);
                return null;
            }
            if (METHOD_COMMIT.equals(method.getName())) {
                doCommit();
                return null;
            }
            if (METHOD_ROLLBACK.equals(method.getName())) {
                doRollback();
                return null;
            }
        }

        //根据路由获取真正的连接，去执行正式的方法
        Object lookupKey = dynamicDataSourceContext.determineCurrentLookupKey();

        Connection realConnection = connectionMap.get(lookupKey);
        //如果已经存在，则无需再从数据源中获取
        if (realConnection == null) {
            realConnection = dynamicDataSourceContext.getRealConnection();

            //设置是否自动提交
            if (!autoCommit) {
                realConnection.setAutoCommit(false);
            }
            connectionMap.put(lookupKey, realConnection);
        }

        //执行
        return method.invoke(realConnection, args);
    }

    /**
     * 关闭所有连接
     */
    private void doClose() throws SQLException {
        for (Connection connection : connectionMap.values()) {
            connection.close();
        }
    }

    /**
     * 设置自动提交
     */
    private void doSetAutoCommit(Object[] args) throws SQLException {
        this.autoCommit = (Boolean) args[0];
        for (Connection connection : connectionMap.values()) {
            connection.setAutoCommit(autoCommit);
        }
    }

    /**
     * 统一提交
     */
    private void doCommit() throws SQLException {
        for (Connection connection : connectionMap.values()) {
            connection.commit();
        }
    }

    /**
     * 统一回滚
     */
    private void doRollback() throws SQLException {
        for (Connection connection : connectionMap.values()) {
            connection.rollback();
        }
    }

}

